local WOWHEAD = WOWHEAD or "http://fr.wowhead.com/"

local ZONE_LIST_URL = WOWHEAD .. "zones"
local ZONE_URL_P = WOWHEAD .. "zone=%d"
local NPC_URL_P = WOWHEAD .. "npc=%d"
local ITEMS_URL_P = WOWHEAD .. "items=%d"
local ITEM_URL_P = WOWHEAD .. "item=%d"

local L
log = function(level, s, ...)
	if not L then
		L = io.open("get_item_data.log", "w")
	end
	if s:find("%", nil, true) and select("#", ...) > 0 then
		L:write(level, "\t", s:format(...))
	else
		L:write(level, "\t", s, ...)
	end
	L:write("\n")
end

-- DOWNLOAD
local curl = require"luacurl"
local c = curl.new()
local function download_page(url)
	log("download_page", url)
	local temp = {}
	c:setopt(curl.OPT_URL, url)
	c:setopt(curl.OPT_WRITEFUNCTION, function (t, s) t[#t + 1] = s return s:len() end)
	c:setopt(curl.OPT_WRITEDATA, temp)
	assert(c:perform())
	local page = table.concat(temp)
	assert(page:len() > 0)
	return page
end

-- CACHE
local sqlite = require"lsqlite3"
local db = sqlite.open("wowhead.db")
db:exec([[
CREATE TABLE IF NOT EXISTS cache (
url TEXT,
content BLOB,
time TEXT,
PRIMARY KEY (url)
)]])

local CACHE_TIMEOUT = CACHE_TIMEOUT or "+7 day"
local select_stmt = db:prepare("SELECT content FROM cache WHERE url = ? AND datetime(time, '"..CACHE_TIMEOUT.."') > datetime('now')")
local insert_stmt = db:prepare("INSERT OR REPLACE INTO cache VALUES (?, ?, CURRENT_TIMESTAMP)")

local function get_page (url)
	select_stmt:bind_values(url)
	local result = select_stmt:step()
	if result == sqlite3.ROW then
		result = select_stmt:get_value(0)
		select_stmt:reset()
		log("get_page ", url, " hit")
		return result
	else
		select_stmt:reset()
	end
	log("get_page ", url, " miss")
	local content = download_page(url)
	if content then
		insert_stmt:bind_values(url, content)
		insert_stmt:step()
		insert_stmt:reset()
	end
	return content
end

-- PARSE
local json = require"json"
json.register_constant("undefined", json.null)
local function get_data(page, view_name, handler)
	local result
	log("get_data", view_name)
	for view in page:gmatch("new Listview(%b())") do
		local name = view:match("id: '([^']+)'")
		if name == view_name then
			result = {}
			local list = view:match("data%: (%b[])")
			for s in list:gmatch("%b{}") do
				local status, entry = pcall(json.decode, s)
				if not status then
					print("-- DECODE FAIL", s) io.flush()
				else
					result[#result + 1] = handler and handler(entry) or entry
				end
			end
		end
	end
	return result
end

-- DUMP RESULT
local HEADER = [[
-- Generated data, do not edit
local addon, DB = ...
]]

local function dump_table(name, table, f)
	f:write(("DB.%s = {\n"):format(name))
	local k, v = next(table)
	local t = type(v)
	local FMT = t == "string" and "\t[%d] = %q,\n" or "\t[%d] = %d,\n"
	for id, value in pairs(table) do
		f:write(FMT:format(id, value))
	end
	f:write(("}\n"):format(name))
end

local function dump(name, data)
	local f = io.open(("../%s.lua"):format(name), "w")
	f:write(HEADER)
	for name, table in pairs(data) do
		dump_table(name, table, f)
	end
	f:close()
end

-- ZONE DROP
local ALLOWED_ZONE_CATEGORIES = {
	[2] = true, -- dungeon
	[3] = true, -- raid
}
local ALLOWED_QUALITY_LEVELS = {
	[1] = true,
	[2] = true,
	[3] = true,
	[4] = true,
}
local DROP_TYPES = { "normal-drops", "heroic-drops", "normal-10-drops", "heroic-10-drops", "normal-25-drops", "heroic-25-drops"}
local SHORT_DROP_TYPES = {
	["normal-drops"] = "N",
	["heroic-drops"] = "H",
	["normal-10-drops"] = "N1",
	["heroic-10-drops"] = "H1",
	["normal-25-drops"] = "N2",
	["heroic-25-drops"] = "H2",
}

local zones = get_data(get_page(ZONE_LIST_URL), 'zones')

for _, zone in ipairs(zones) do
	log("zone", zone.name)
	if ALLOWED_ZONE_CATEGORIES[zone.category] then
		local npcs = get_data(get_page(ZONE_URL_P:format(zone.id)), 'npcs')
		if npcs then
			local bosses = {}
			for _, npc in ipairs(npcs) do
				if npc.boss then
					log("boss", zone.name, "\t", npc.name)
					local page = get_page(NPC_URL_P:format(npc.id))
					for _, drop_type in ipairs(DROP_TYPES) do
						npc[drop_type] = get_data(page, drop_type)
					end
					bosses[#bosses + 1] = npc
				end
			end
			zone.bosses = bosses
		end
	end
end

local itemList = {}

local function append(table, value)
	for _, v in ipairs(table) do
		if v == value then return end
	end
	table[#table + 1] = value
end

for _, zone in ipairs(zones) do
	if zone.bosses then
		for _, boss in ipairs(zone.bosses) do
			for _, drop_type in ipairs(DROP_TYPES) do
				local list = boss[drop_type]
				if list then
					for _, item in ipairs(list) do
						local quality, name = item.name:match("^(%d)(.*)$")
						quality = tonumber(quality)
						log("item", zone.name, "\t", boss.name, "\t", drop_type, "\t", quality, "\t", name, "\t", item.slot)
						if ALLOWED_QUALITY_LEVELS[quality] and item.slot > 0 then
							local entry = itemList[item.id]
							if not entry then
								entry = {
									drop_types = {drop_type},
									name = item.name,
									zones = {zone.name},
									bosses = {boss.name}
								}
								itemList[item.id] = entry
							else
								append(entry.drop_types, drop_type)
								append(entry.zones, zone.name)
								append(entry.bosses, boss.name)
							end
							log("item", tostring(item.id), "\t", "ADDED")
						end
					end
				end
			end
		end
	end
end

local zones, bosses, drop_types = {}, {}, {}

for id, item in pairs(itemList) do
	zones[id] = #item.zones == 1 and item.zones[1] or "M"
	bosses[id] = #item.bosses == 1 and item.bosses[1] or "M"
	drop_types[id] = SHORT_DROP_TYPES[item.drop_types[1]] -- sorted so it's fine to take the lowest
end

dump("drops", {
	zones = zones,
	bosses = bosses,
	drop_types = drop_types,
})

-- PURCHASABLE ITEMS
local costs, currencies = {}, {}

for _, currency in ipairs(get_data(get_page(ITEMS_URL_P:format(10)), 'items')) do
	local currency_id = currency.id
	local purchases = get_data(get_page(ITEM_URL_P:format(currency_id)), 'currency-for')
	if purchases then
		for _, purchase in ipairs(purchases) do
			local purchase_id = purchase.id
			local cost
			local entries = purchase.cost[4]
			if #entries == 1 and entries[1][1] == currency_id then
				cost = entries[1][2]
			end
			if cost and purchase.slot > 0 then
				if not currencies[purchase_id] or currency_id < currencies[purchase_id] then
					currencies[purchase_id] = currency_id
					costs[purchase_id] = cost
				end
			end
		end
	end
end

dump("costs", {
	costs = costs,
	currencies = currencies,
})
